package de.stefanocke.roo.wicket;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.ProjectType;
import org.springframework.roo.project.Property;
import org.springframework.roo.project.Repository;
import org.apache.commons.lang3.Validate;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.WebXmlUtils;
import org.springframework.roo.support.util.WebXmlUtils.FilterPosition;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import de.stefanocke.roo.wicket.templates.TemplateSupportService;
import de.stefanocke.roo.wicket.templates.Templates;
import de.stefanocke.roo.wicket.templates.WicketApplicationTemplate;

/**
 * Implementation of operations this add-on offers.
 * 
 * @since 1.1
 */
@Component
// Use these Apache Felix annotations to register your commands class in the Roo
// container
@Service
public class WicketOperationsImpl implements WicketOperations {
	private String OPEN_ENTITYMANAGER_IN_VIEW_FILTER_NAME = "Spring OpenEntityManagerInViewFilter";
	private String WICKET_FILTER_NAME = "Wicket Filter";

	/**
	 * Use ProjectOperations to install new dependencies, plugins, properties,
	 * etc into the project configuration
	 */
	@Reference
	private ProjectOperations projectOperations;

	@Reference
	TemplateSupportService templateSupportService;

	@Reference
	ApplicationService applicationService;

	@Reference
	private FileManager fileManager;

	/** {@inheritDoc} */
	public boolean isSetupCommandAvailable() {
		// Check if a project has been created
		return projectOperations.isFocusedProjectAvailable();
	}

	/** {@inheritDoc} */
	public void setup(JavaPackage webPckg, JavaPackage domainPckg, boolean forceUpdate) {
		String moduleName = projectOperations.getFocusedModuleName();
		managePOM(moduleName);
		copyWebXml(moduleName);

		JavaType applicationClass = createCommonArtifactsFromTemplates(moduleName, webPckg, domainPckg, forceUpdate);
		manageWebXml(moduleName, applicationClass);
	}

	/**
	 * Creates common artifacts from templates an returns the application class.
	 * 
	 * @param domainPckg
	 */
	private JavaType createCommonArtifactsFromTemplates(String moduleName, JavaPackage webPckg, JavaPackage domainPckg,
			boolean forceUpdate) {
		// TODO: The domain package might be in another module...
		Templates templates = new Templates(templateSupportService, applicationService, projectOperations, moduleName,
				webPckg, domainPckg);
		templates.createOrUpdateFiles(forceUpdate);
		return templates.getTemplate(WicketApplicationTemplate.class).getJavaType();
	}

	private void managePOM(String moduleName) {
		// Install the add-on Google code repository needed to get the
		// annotation
		// TODO!
		projectOperations.addRepository(moduleName, new Repository("Wicket Roo add-on repository",
				"Wicket Roo add-on repository", "http://wicket4roo.googlecode.com/svn/repo"));

		List<Dependency> dependencies = new ArrayList<Dependency>();

		// Install dependencies defined in external XML file
		for (Element dependencyElement : XmlUtils.findElements("/configuration/wicket/dependencies/dependency",
				XmlUtils.getConfiguration(getClass()))) {
			dependencies.add(new Dependency(dependencyElement));
		}

		// Add all new dependencies to pom.xml
		projectOperations.addDependencies(moduleName, dependencies);

		projectOperations.addProperty(moduleName, new Property("wicket.version", "6.2.0"));

		projectOperations.updateProjectType(moduleName, ProjectType.WAR);
	}

	private void copyWebXml(String moduleName) {
		Validate.isTrue(projectOperations.isProjectAvailable(moduleName), "Project metadata required");
		PathResolver pathResolver = projectOperations.getPathResolver();

		String webXmlPath = getWebXmlPath(moduleName, pathResolver);
		if (fileManager.exists(webXmlPath)) {
			// File exists, so nothing to do
			return;
		}

		Document document;
		try {
			InputStream templateInputStream = FileUtils.getInputStream(getClass(), "web-template.xml");
			Validate.notNull(templateInputStream, "Could not acquire web.xml template");
			document = XmlUtils.getDocumentBuilder().parse(templateInputStream);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}

		String projectName = projectOperations.getProjectName(moduleName);
		WebXmlUtils.setDisplayName(projectName, document, null);
		WebXmlUtils.setDescription("Roo generated " + projectName + " application", document, null);

		fileManager.createOrUpdateTextFileIfRequired(webXmlPath, XmlUtils.nodeToString(document), true);
	}

	private String getWebXmlPath(String moduleName, PathResolver pathResolver) {
		LogicalPath webappLPath = LogicalPath.getInstance(Path.SRC_MAIN_WEBAPP, moduleName);
		String webXmlPath = pathResolver.getIdentifier(webappLPath, "WEB-INF/web.xml");
		return webXmlPath;
	}

	private void manageWebXml(String moduleName, JavaType applicationClass) {
		Validate.isTrue(projectOperations.isProjectAvailable(moduleName), "Project metadata required");
		PathResolver pathResolver = projectOperations.getPathResolver();

		// Verify that the web.xml already exists
		String webXmlPath = getWebXmlPath(moduleName, pathResolver);
		Validate.isTrue(fileManager.exists(webXmlPath), "'" + webXmlPath + "' does not exist");

		Document document = XmlUtils.readXml(fileManager.getInputStream(webXmlPath));

		WebXmlUtils.addContextParam(new WebXmlUtils.WebXmlParam("contextConfigLocation",
				"classpath*:META-INF/spring/applicationContext*.xml"), document, null);

		WebXmlUtils.addListener("org.springframework.web.context.ContextLoaderListener", document,
				"Creates the Spring Container shared by all Servlets and Filters");

		WebXmlUtils.addFilter(WICKET_FILTER_NAME, "org.apache.wicket.protocol.http.WicketFilter", "/*", document, null,
				new WebXmlUtils.WebXmlParam("applicationClassName", applicationClass.getFullyQualifiedTypeName()));

		if (fileManager.exists(pathResolver.getIdentifier(LogicalPath.getInstance(Path.SRC_MAIN_RESOURCES, moduleName),
				"META-INF/persistence.xml"))) {
			WebXmlUtils.addFilterAtPosition(FilterPosition.BEFORE, null, WICKET_FILTER_NAME,
					OPEN_ENTITYMANAGER_IN_VIEW_FILTER_NAME,
					"org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter", "/*", document, null);
		}

		// WebXmlUtils.setSessionTimeout(new Integer(10), document, null);

		fileManager.createOrUpdateTextFileIfRequired(webXmlPath, XmlUtils.nodeToString(document), false);
	}

}